package cn.superchart.module.user.service.impl;

import cn.superchart.common.base.BaseServiceImpl;
import cn.superchart.common.base.BaseUserDetails;
import cn.superchart.common.enums.AuthEnum;
import cn.superchart.common.enums.ExceptionEnum;
import cn.superchart.common.exception.ScException;
import cn.superchart.common.properties.SecurityProperties;
import cn.superchart.common.utils.CacheContext;
import cn.superchart.common.utils.JwtUtils;
import cn.superchart.module.dev.api.ScInstallService;
import cn.superchart.module.dev.api.pojo.ScInstallParam;
import cn.superchart.module.menu.service.ScMenuService;
import cn.superchart.module.role.param.ScRoleAssignParam;
import cn.superchart.module.role.service.ScRoleService;
import cn.superchart.module.uerole.param.ScUserRoleAddParam;
import cn.superchart.module.uerole.service.ScUserRoleService;
import cn.superchart.module.user.entity.ScUser;
import cn.superchart.module.user.mapper.ScUserMapper;
import cn.superchart.module.user.param.*;
import cn.superchart.module.user.service.ScUserService;
import com.alicp.jetcache.Cache;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class ScUserServiceImpl extends BaseServiceImpl<ScUser, ScUserMapper> implements ScUserService {

    private final AuthenticationManager authenticationManager;
    private final SecurityProperties securityProperties;
    private final ScInstallService scInstallService;
    private final ScRoleService roleService;

    private final ScUserRoleService scUserRoleService;
    private final PasswordEncoder passwordEncoder;
    private final SecurityContextLogoutHandler securityContextLogoutHandler;
    private final ScMenuService scMenuService;


    public ScUserServiceImpl(AuthenticationManager authenticationManager, SecurityProperties securityProperties, ScInstallService scInstallService, ScRoleService roleService, ScUserRoleService scUserRoleService, ScUserRoleService scUserRoleService1, PasswordEncoder passwordEncoder, SecurityContextLogoutHandler securityContextLogoutHandler, ScMenuService scMenuService) {
        this.authenticationManager = authenticationManager;
        this.securityProperties = securityProperties;
        this.scInstallService = scInstallService;
        this.roleService = roleService;
        this.scUserRoleService = scUserRoleService1;
        this.passwordEncoder = passwordEncoder;
        this.securityContextLogoutHandler = securityContextLogoutHandler;
        this.scMenuService = scMenuService;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(ScUserAddParam scUserAddParam) {
        boolean isEq = scUserAddParam.getPassword().equals(scUserAddParam.getPasswordAgain());
        if(!isEq){
            throw new ScException(ExceptionEnum.NOT_EQ_PW);
        }
        if(hasUser(scUserAddParam.getAccount())){
            throw new ScException(ExceptionEnum.HAS_USER_ACCOUNT);
        }
        String password = passwordEncoder.encode(scUserAddParam.getPassword());
        scUserAddParam.setPassword(password);
        String pk = super.saveData(scUserAddParam);
        log.info(pk);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(ScUserEditParam scUserEditParam) {
         scUserEditParam.setAccount(null);
         scUserEditParam.setPassword(null);
         super.updateData(scUserEditParam);
    }

    @Override
    public ScUserDetailParam detail(String id) {
        return queryData(id,ScUserDetailParam.class);
    }

    @Override
    public String blogin(ScLoginParam scLoginParam) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(scLoginParam.getAccount(), scLoginParam.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        BaseUserDetails principal = (BaseUserDetails) authenticate.getPrincipal();
        return JwtUtils.createToken(securityProperties.getJwt().getExpire(),principal.getUserId(),principal.getUsername());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean register(ScUserRegisterParam scUserRegisterParam) {
        if(scInstallService.verify()){
            throw new ScException(ExceptionEnum.INSTALLED_SYS);
        }
        boolean isEq = scUserRegisterParam.getPassword().equals(scUserRegisterParam.getPasswordAgain());
        if(!isEq){
            throw new ScException(ExceptionEnum.NOT_EQ_PW);
        }
        if(hasUser(scUserRegisterParam.getAccount())){
            throw new ScException(ExceptionEnum.HAS_USER_ACCOUNT);
        }
        ScUser scUser = copyProperties(scUserRegisterParam,ScUser.class);
        initUserEnv(scUser);
        return installFlag(scUserRegisterParam);
    }

    @Override
    public ScUser queryByAccount(String account) {
        QueryWrapper<ScUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ScUser::getAccount,account);
        return super.getOne(queryWrapper);
    }

    @Override
    public ScUserDetailParam getUserInfo() {
        String userId = getUserId();
        ScUser scUser = queryEntity(userId);
        ScUserDetailParam scUserDetailParam = copyProperties(scUser,ScUserDetailParam.class);
        List<String>  perms = scMenuService.getPerms(userId);
        scUserDetailParam.setPerms(perms);
        return scUserDetailParam;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            // 记录退出日志
            log.info("User {} logged out.",authentication.getName() );
            // 调用 Spring Security 默认注销逻辑
            securityContextLogoutHandler.logout(request, response, authentication);
            CacheContext.removeUserCache(getUserId());
        }
    }

    @Override
    public Page<ScUserDetailParam> page(ScUserPageParam userPageParam) {
//        QueryWrapper<ScUser> queryWrapper =queryWrapper(userPageParam);
//        Page<ScUser> page = super.page(userPageParam.defaultPage(),queryWrapper);
//        Page<ScUserDetailParam> pageRes =copyProperties(page,ScUserDetailParam.class);
//        return pageRes;
          return super.page(userPageParam,ScUserDetailParam.class);
    }

    @Override
    public void unOrLock(String id, String userStatus) {
        if(!Arrays.asList("1","2").contains(userStatus)){
            throw new ScException(70007,"状态异常!");
        }
        baseMapper.unOrLock(id,userStatus);
    }


    @Transactional
    @Override
    public void roleAssign(ScRoleAssignParam roleAssignParam) {
        String userId = roleAssignParam.getUserId();
        scUserRoleService.removeByUid(userId);
        List<ScUserRoleAddParam> scUserRoleAddParams = new ArrayList<>();
        for(String roleId:roleAssignParam.getRoleIds()){
            ScUserRoleAddParam scUserRoleAddParam = new ScUserRoleAddParam();
            scUserRoleAddParam.setUserId(userId);
            scUserRoleAddParam.setRoleId(roleId);
            scUserRoleAddParams.add(scUserRoleAddParam);
        }
        scUserRoleService.saveData(scUserRoleAddParams);
    }

    @Override
    public List<String> roleIds(String userId) {
        return scUserRoleService.queryRoleIdByUid(userId);
    }

    private boolean hasUser(String account){
        QueryWrapper<ScUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(ScUser::getAccount,account);
        return super.count(queryWrapper) > 0;
    }

    private boolean installFlag(ScUserRegisterParam scUserRegisterParam){
        ScInstallParam scInstallParam = new ScInstallParam();
        copyProperties(scUserRegisterParam,scInstallParam);
        scInstallParam.setSalt(UUID.randomUUID().toString());
        return scInstallService.installed(scInstallParam);
    }

    private void initUserEnv(ScUser scUser){
        String password = passwordEncoder.encode(scUser.getPassword());
        scUser.setPassword(password);
        super.save(scUser);
        String userId = scUser.getId();
        roleService.initInstall(userId);
    }

}
